﻿using System;
using System.Collections.Generic;
using System.Threading;
using Cysharp.Threading.Tasks;
using Internal.Runtime.Models;
using Internal.Runtime.Services.Battle;
using KuiHuaBaoDian.Services.Asset;
using Runtime.Models;
using Runtime.Services.Battle.Entity;
using UnityEngine;
using UnityEngine.Tilemaps;

namespace Runtime.Services.Battle {

    /// <summary>
    /// 战斗服务
    /// </summary>
    public interface IDFR_BattleServiceTD : IDFR_BattleServiceBase {

        /// <summary>
        /// 取得路径
        /// </summary>
        /// <param name="id">路径ID</param>
        IDFR_BattleWay GetWay(DFR_BattleWayID id);

        /// <summary>
        /// 添加路径
        /// </summary>
        /// <param name="way">路径</param>
        void AddWay(IDFR_BattleWay way);

        /// <summary>
        /// 取得地图位置
        /// </summary>
        /// <param name="wayID">路径ID</param>
        /// <param name="wayPosition">路径位置</param>
        DFR_BattleMapPosition GetMapPosition(DFR_BattleWayID wayID, F64 wayPosition, bool isReverse);

        DFR_BattleCellAxis GetWayNextAxis(IDFR_BattleWay way, F64 wayPosition, bool isReverse);
    }

    public static class BattleServiceTD {

        /// <summary>
        /// 单例
        /// </summary>
        public static IDFR_BattleServiceTD Instance => m_Instance ??= (IDFR_BattleServiceTD)BattleServiceBase.Instance;
        private static IDFR_BattleServiceTD m_Instance;
    }

    public abstract class DFR_BattleServiceTD<TRecord> : DFR_BattleServiceBase<TRecord> where TRecord : DFR_BattleEntityRecordBase {

        protected sealed class CellRecord : IDisposable {

            public DFR_BattleCellID ID;
            public CancellationTokenSource CTS = new();
            public AssetHandle<TileBase> Asset;
            public IDFR_BattleCell Entity;

            public void Dispose() {
                if (Asset != AssetHandle.None) {
                    AssetService.Release(Asset);
                    Asset = AssetHandle.None;
                } else {
                    CTS.Cancel();
                }
                CTS = null;
                Entity = null;
            }
        }

        public override uint HorizontalCellCount => DFR_BattleTD.DataCenter.Models.GridOption.HorizontalCount;
        public override uint VerticalCellCount => DFR_BattleTD.DataCenter.Models.GridOption.VerticalCount;

        private Func<IDFR_BattleWay, F64, bool, DFR_BattleCellAxis> m_GetWayNextAxis;

        private readonly IDictionary<DFR_BattleWayID, IDFR_BattleWay> m_WayDictionary = new Dictionary<DFR_BattleWayID, IDFR_BattleWay>();

        private IDFR_BattleCellDictionaryModelTD m_CellModelDictionary;
        private IDFR_BattleAttackSourceDictionaryModelTD m_AttackSourceModelDictionary;
        private IDFR_BattleAttackTargetDictionaryModelTD m_AttackTargetModelDictionary;

        public IDFR_BattleWay GetWay(DFR_BattleWayID id) => m_WayDictionary[id];

        public void AddWay(IDFR_BattleWay way) => m_WayDictionary[way.ID] = way;

        protected override void Prepare(DFR_BattlePlayerID myPlayerID) {
            base.Prepare(myPlayerID);
            m_CellModelDictionary = DFR_BattleTD.DataCenter.Models.CellDictionary.AsReadWrite();
            m_AttackSourceModelDictionary = DFR_BattleTD.DataCenter.Models.AttackSourceDictionary.AsReadWrite();
            m_AttackTargetModelDictionary = DFR_BattleTD.DataCenter.Models.AttackTargetDictionary.AsReadWrite();
        }

        protected void Prepare(Func<IDFR_BattleWay, F64, bool, DFR_BattleCellAxis> getWayNextAxis) {
            m_GetWayNextAxis = getWayNextAxis;
        }

        public DFR_BattleMapPosition GetMapPosition(DFR_BattleWayID wayID, F64 wayPosition, bool isReverse) {
            var way = GetWay(wayID);
            var currentWayPositionInt = wayPosition.Int;
            var currentPoint = way.Get(way.Count - (currentWayPositionInt + 1), isReverse);
            var nextAxis = way.GetNextAxis(wayPosition, isReverse);
            var sectionInterpolation = wayPosition - currentWayPositionInt;
            var lerp = Vector2F64.Lerp(currentPoint.Axis, nextAxis, sectionInterpolation);
            return new() { X = lerp.X, Y = lerp.Y };
        }

        protected override IDFR_BattleMapPositionProvider GetMapPositionProvider(DFR_BattleEntityInstanceID entityInstanceID) {
            if (entityInstanceID.ID.Type == DFR_BattleEntityType.AttackTarget) {
                if (m_AttackTargetModelDictionary.TryGetValue(entityInstanceID, out var target) && target is IDFR_BattleMapPositionProvider result) {
                    return result;
                }
            } else if (entityInstanceID.ID.Type == DFR_BattleEntityType.AttackSource) {
                if (m_AttackSourceModelDictionary.TryGetValue(entityInstanceID, out var source) && source is IDFR_BattleMapPositionProvider result) {
                    return result;
                }
            } else {
                throw new NotSupportedException();
            }
            return null;
        }

        protected override DFR_BattleMapPosition GetMapPosition(DFR_BattleEntityInstanceID entityInstanceID) {
            if (entityInstanceID.ID.Type == DFR_BattleEntityType.AttackTarget) {
                if (m_AttackTargetModelDictionary.TryGetValue(entityInstanceID, out var target) && target is IDFR_BattleMapPositionProvider provider) {
                    return provider.MapPosition;
                } else if (target is IDFR_BattleMapPositionGetter getter) {
                    return getter.MapPosition;
                }
            } else if (entityInstanceID.ID.Type == DFR_BattleEntityType.AttackSource) {
                if (m_AttackSourceModelDictionary.TryGetValue(entityInstanceID, out var source) && source is IDFR_BattleMapPositionProvider provider) {
                    return provider.MapPosition;
                } else if (source is IDFR_BattleMapPositionGetter getter) {
                    return getter.MapPosition;
                }
            } else {
                throw new NotSupportedException();
            }
            return default;
        }

        public DFR_BattleCellAxis GetWayNextAxis(IDFR_BattleWay way, F64 wayPosition, bool isReverse) => m_GetWayNextAxis.Invoke(way, wayPosition, isReverse);

        protected override void End() {
            foreach (var kv in m_WayDictionary) {
                (kv.Value as IDisposable).Dispose();
            }
            m_WayDictionary.Clear();
            m_CellModelDictionary = null;
            m_AttackSourceModelDictionary = null;
            m_AttackTargetModelDictionary = null;

            m_GetWayNextAxis = null;
            base.End();
        }

        protected override void DisposeModel() {
            (DFR_BattleTD.DataCenter as IDisposable).Dispose();
        }

        protected virtual async UniTask FillGrid(string assetKey) {
            var battleModel = DFR_BattleTD.DataCenter.Models;
            // TODO: 多人或多路径需要强化路径来源
            var wayID = new DFR_BattleWayID { Value = m_WayDictionary.Count + 1 };
            m_WayDictionary.Add(wayID, new DFR_BattleWay(battleModel.Way) { ID = wayID });
            foreach (var kv in m_WayDictionary) {
                var way = kv.Value;
                var cells = new IDFR_BattleCell[DFR_BattleTD.DataCenter.Models.GridOption.CellCount];

                IDFR_BattleCell lastCell = null;
                for (var i = 0; i < way.Count; i++) {
                    var point = way.Get(i, false);
                    var index = point.Axis.ToIndex();
                    var cellModel = KHBD.TypeMap.Instantiate<IDFR_BattleCellModelTD>();
                    (cellModel as IDFR_BattleCellModelTDInternal).Initialize(index, false, DFR_BattlePlayerID.None, DFR_BattleEntityType.Cell);
                    m_CellModelDictionary.Add(index, cellModel);
                    var cell = lastCell = new DFR_BattleCell {
                        ID = new() { Axis = point.Axis, Index = index },
                        RoadNextID = lastCell == null ? DFR_BattleCellID.Invalid : lastCell.ID,
                    };
                    cells[index.Value] = cell;
                }

                var tasks = new List<UniTask>();
                for (var i = 0; i < DFR_BattleTD.DataCenter.Models.GridOption.CellCount; i++) {
                    var cell = cells[i];
                    var index = new DFR_BattleCellIndex { Value = i };
                    var axis = index.ToAxis();
                    if (!m_CellModelDictionary.TryGetValue(index, out var cellModel) && !battleModel.IsStaticObstacle(axis)) {
                        cellModel = KHBD.TypeMap.Instantiate<IDFR_BattleCellModelTD>();
                        // TODO: 目前只有单机模式，全是我的格子, 多人模式则需要来源
                        (cellModel as IDFR_BattleCellModelTDInternal).Initialize(index, true, MyPlayerID, DFR_BattleEntityType.Cell);
                        m_CellModelDictionary.Add(index, cellModel);
                        cell ??= cells[i] = new DFR_BattleCell() {
                            ID = new() { Axis = axis, Index = index },
                            RoadNextID = DFR_BattleCellID.Invalid,
                        };
                    }
                    if (cellModel != null) {
                        (cell as IDFR_BattleCellInternal).Initialize(cellModel);
                        var record = new CellRecord {
                            ID = cell.ID,
                            Entity = cell,
                        };
                        var task = InstantiateCell(record, assetKey);
                        tasks.Add(task);
                    }
                }
                await UniTask.WhenAll(tasks);
            }
        }

        protected abstract UniTask InstantiateCell(CellRecord record, string assetKey);
    }
}
